//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#include "NjExample01.h"

#if defined(NJ_PLATFORM_WIN32) || defined(NJ_PLATFORM_WIN64)
#	include "NjThreadsSystemWin.h"

	//----------------------------------------------------------------------------
	class NjThreadsSystemWin::Static
	{
	public:
		struct Data
		{
			ThreadFunct m_pfnThread;
			void * m_pThreadData;
		};

		static DWORD WINAPI ThreadProc(LPVOID pParameter);
		static DWORD ToWinTime(TimeMs uTimeMs);

	private:
		NJ_DISABLE_INSTANCE(Static);
	};
	//----------------------------------------------------------------------------
	NjThreadsSystemWin::Thread * NjThreadsSystemWin::CreateThread(ThreadFunct pfnThread, void * pData)
	{
		Static::Data * pParameter(NjNew(Static::Data));
		pParameter->m_pfnThread=pfnThread;
		pParameter->m_pThreadData=pData;

		HANDLE kThread(::CreateThread(NJ_NULL, 0, Static::ThreadProc, pParameter, 0, NJ_NULL));
		if (kThread==NJ_NULL)
		{
			NjDelete(pParameter);
			return NJ_NULL;
		}

		return reinterpret_cast<Thread *>(kThread);
	}
	//----------------------------------------------------------------------------
	NjThreadsSystemWin::TryResult NjThreadsSystemWin::TryJoinThread(Thread * pThread, TimeMs uTimeMs)
	{
		if (pThread==NJ_NULL)
			return NjEventsSystem::TRY_FAILURE;

		DWORD const iResult(::WaitForSingleObject(reinterpret_cast<HANDLE>(pThread), Static::ToWinTime(uTimeMs)));
		
		if (iResult==WAIT_OBJECT_0)
			return NjEventsSystem::TRY_SUCCESS;
		
		if (iResult==WAIT_TIMEOUT)
			return NjEventsSystem::TRY_TIMEOUT;
		
		return NjEventsSystem::TRY_FAILURE;
	}
	//----------------------------------------------------------------------------
	NjBool NjThreadsSystemWin::DestroyThread(Thread * pThread)
	{
		if (pThread==NJ_NULL)
			return NJ_FALSE;

		HANDLE const kThread(reinterpret_cast<HANDLE>(pThread));
		NjBool const bSuccess(::TerminateThread(kThread, 1)!=0);
		::CloseHandle(kThread);

		return bSuccess;
	}
	//----------------------------------------------------------------------------
	NjBool NjThreadsSystemWin::Sleep(TimeMs uTimeMs)
	{
		::Sleep(DWORD(uTimeMs));
		return NJ_TRUE;
	}
	//----------------------------------------------------------------------------
	NjThreadsSystem::YeldResult NjThreadsSystemWin::Yeld()
	{
		if (::SwitchToThread()!=0)
			return NjThreadsSystem::YELD_SWITCHED_SUCCESS;

		return NjThreadsSystem::YELD_NOT_SWITCHED_SUCCESS;
	}
	//----------------------------------------------------------------------------
	DWORD NjThreadsSystemWin::Static::ThreadProc(LPVOID pParameter)
	{
		if (pParameter==NJ_NULL)
			return 0;

		Data * const pData(reinterpret_cast<Data *>(pParameter));

		ThreadFunct pfnThread(pData->m_pfnThread);
		void * pThreadData(pData->m_pThreadData);
		NjDelete(pData);

		pfnThread(pThreadData);

		return 0;
	}
	//----------------------------------------------------------------------------
	inline DWORD NjThreadsSystemWin::Static::ToWinTime(TimeMs uTimeMs)
	{
		if (uTimeMs==NjEventsSystem::WAIT_FOREVER)
			return INFINITE;
		else
			return DWORD(uTimeMs);
	}
	//----------------------------------------------------------------------------

#endif // defined(NJ_PLATFORM_WIN32) || defined(NJ_PLATFORM_WIN64)
